package com.supreme.studyserver.service;

import com.supreme.studyserver.config.TaskExecutorConfig;
import com.supreme.studyserver.thread.CallableThread;
import com.supreme.studyserver.thread.ExtThread;
import com.supreme.studyserver.thread.ImplThread;
import com.supreme.studyserver.thread.ThreadPoolException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;

import java.util.concurrent.*;
import java.util.function.*;

@Service
public class TaskScheduleService {

//    @Autowired
//    private ThreadPoolTaskExecutor applicationTaskExecutor;

    public void demoThread() {
        final Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println("demoThread: " + i);
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Thread + Runnable
     */
    public void demoRunnableThread() {
        final Runnable runnable = () -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println("demoRunnableThread: " + i);
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        final Thread runnableThread = new Thread(runnable, "runnableThread");
        runnableThread.start();
        System.out.println(runnableThread.getName());
        try {
            runnableThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Thread + Class + Runnable
     */
    public void demoThreadImpl() {
        final Thread thread = new Thread(new ImplThread(10));
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("demoThreadImpl finish");
    }

    /**
     * Thread + Class + Runnable
     */
    public void demoThreadImpl2() {
        final ImplThread implThread = new ImplThread(count -> {
            for (int i = 0; i < count; i++) {
                try {
                    System.out.println("ImplThread: " + i);
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 8);

        final Thread thread = new Thread(implThread);
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("demoThreadImpl finish");
    }

    /**
     * Thread + Class
     */
    public void demoThreadExt() {
        final ExtThread thread = new ExtThread(10);
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * FutureTask + Callable + Thread
     */
    public void demoFutureCallable() {
        final CallableThread callableThread = new CallableThread(count -> {
            for (int i = 0; i < count; i++) {
                try {
                    System.out.println("CallableThread: " + i);
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 5);

        final FutureTask<Integer> integerFutureTask = new FutureTask<>(callableThread);
        final Thread futureThread = new Thread(integerFutureTask);
        futureThread.start();

        try {
            final Integer futureTaskResult = integerFutureTask.get();
//            futureThread.join();
            System.out.println("futureTaskResult: " + futureTaskResult);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("demoFutureCallable finish");
    }

    /**
     * CompletableFuture + supplyAsync + thenApply + whenComplete + handle + thenAccept
     */
    public void demoCompletableFuture() {
        System.out.println("mainThreadName: " + Thread.currentThread().getName());

        int count = 5;
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                System.out.println("[supplyAsync] ThreadName: " +  Thread.currentThread().getName());
                int sumAmount = 0;

                for (int i = 0; i < count; i++) {
                    try {
                        System.out.println("CompletableFuture get: " + i);
                        sumAmount += i;
                        TimeUnit.MILLISECONDS.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                return sumAmount;
            }
        }).thenApply(new Function<Integer, Integer>() {
            @Override
            public Integer apply(Integer integer) {
                System.out.println("[thenApply] ThreadName: " + Thread.currentThread().getName());
                System.out.println("CompletableFuture thenApply: " + integer);
                return integer * 10;
            }
        }).whenComplete(new BiConsumer<Integer, Throwable>() {
            @Override
            public void accept(Integer integer, Throwable throwable) {
                System.out.println("[whenComplete] ThreadName: " + Thread.currentThread().getName());
                System.out.println("CompletableFuture whenComplete: " + integer);
            }
        }).handle(new BiFunction<Integer, Throwable, Integer>() {
            @Override
            public Integer apply(Integer integer, Throwable throwable) {
                System.out.println("[handle] ThreadName: " + Thread.currentThread().getName());
                System.out.println("CompletableFuture handle: " + integer);
                return integer * 10;
            }
        });

        future.thenAccept(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println("[thenAccept] ThreadName: " + Thread.currentThread().getName());
                System.out.println("CompletableFuture thenAccept: " + integer);
            }
        });

        try {
            final Integer futureResult = future.get();
            System.out.println("futureResult: " + futureResult);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * CompletableFuture + supplyAsync + allOf + thenAccept
     */
    public void demoCompletableFutureAAO() {
        final CompletableFuture<Integer> leftFuture = CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                try {
                    System.out.println("leftFuture begin: " + Thread.currentThread().getName());
                    TimeUnit.MILLISECONDS.sleep(300);
                    System.out.println("leftFuture end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 99;
            }
        });

        final CompletableFuture<String> rightFuture = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    System.out.println("rightFuture begin: " + Thread.currentThread().getName());
                    TimeUnit.MILLISECONDS.sleep(200);
                    System.out.println("rightFuture end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "Future";
            }
        });

        final CompletableFuture<Void> lrFuture = CompletableFuture.allOf(leftFuture, rightFuture).thenAccept(new Consumer<Object>() {
            @Override
            public void accept(Object o) {
                System.out.println("lrFuture: " + Thread.currentThread().getName());
                System.out.println("lrFuture: " + o);
            }
        });

        try {
            final Object lrFutureResult = lrFuture.get();
            System.out.println("lrFutureResult: " + lrFutureResult);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * ThreadPoolExecutor + Future + Callable
     */
    public void demoThreadPoolCallable() {
        final CallableThread callableThread = new CallableThread(count -> {
            for (int i = 0; i < count; i++) {
                try {
                    System.out.println("CallableThread: " + i);
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 5);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 1000L, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolException());

        final Future<Integer> threadPoolSubmit = threadPoolExecutor.submit(callableThread);
        try {
            final Integer threadPoolResult = threadPoolSubmit.get();
            System.out.println("threadPoolResult: " + threadPoolResult);
            threadPoolExecutor.shutdown();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * ThreadPoolExecutor + Future + Thread + Runnable
     */
    public void demoThreadPool() {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 1000L, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolException());

        final Thread thread = new Thread(new ImplThread(10));
        final Future<?> submitResult = threadPoolExecutor.submit(thread);

        try {
            submitResult.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("demoThreadPool finish");
    }

    /**
     * FixedThreadPool + Future
     */
    public void demoFixedThreadPool() {
        final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        final Future<?> submit = fixedThreadPool.submit(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println("demoRunnableThread: " + i);
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        try {
            submit.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("demoFixedThreadPool finish");
    }

    public void demoFourExecutorTask() {
        Executors.newFixedThreadPool(2);
        final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
    }

    /**
     * ThreadPoolExecutor + Future
     */
    public void demoThreadPoolTask() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(5);
        threadPoolTaskExecutor.setMaxPoolSize(50);
        threadPoolTaskExecutor.initialize();
        final Future<Integer> submit = threadPoolTaskExecutor.submit(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println("demoThreadPoolTask: " + i);
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            return 1099;
        });
        try {
            final Integer futureResult = submit.get();
            System.out.println("demoThreadPoolTask futureResult: " + futureResult);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("demoThreadPoolTask finish");
    }

    /**
     * ForkJoinPool 测试
     */
    public void demoForkJoinPool() {
        System.out.println("threadName: " + Thread.currentThread().getName());

        ForkJoinPool forkJoinPool0 = new ForkJoinPool();
        forkJoinPool0.invoke(new RecursiveAction() {
            @Override
            protected void compute() {
                System.out.println("threadName: " + Thread.currentThread().getName());
            }
        });
        System.out.println("RecursiveAction");

        final ForkJoinPool forkJoinPool1 = new ForkJoinPool();
        final Integer recursiveResult = new ForkJoinPool().invoke(new RecursiveTask<Integer>() {
            @Override
            protected Integer compute() {
                System.out.println("threadName: " + Thread.currentThread().getName());
                return 99;
            }
        });
        System.out.println("recursiveResult: " + recursiveResult);

        forkJoinPool0.shutdown();
        forkJoinPool1.shutdown();
    }

    /**
     *
     */
    public void demoNormalFunction() {
        System.out.println("demoNormalFunction threadName: " + Thread.currentThread().getName());
    }

    /**
     * Async + Void
     */
    @Async(value = TaskExecutorConfig.ASYNC_TASK)
    public Future<String> demoAsyncFunction() {
        final String threadName = Thread.currentThread().getName();
        for (int i = 0; i < 20; i++) {
            try {
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("demoAsyncFunction threadName: " + threadName);
        }

        return new AsyncResult<>(threadName);
    }

    /**
     * Async + Future + AsyncResult
     */
    @Async(value = TaskExecutorConfig.ASYNC_TASK)
    public Future<String> demoAsyncFuture() {
        final String threadName = Thread.currentThread().getName();
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("demoAsyncFuture threadName: " + threadName);
        return new AsyncResult<>(threadName);
    }

    /**
     * Async + ListenableFuture + AsyncResult
     */
    @Async(value = TaskExecutorConfig.ASYNC_TASK)
    public ListenableFuture<Integer> demoAsyncListenableFuture() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("demoAsyncListenableFuture threadName: " + Thread.currentThread().getName());
        return new AsyncResult<>(987);
    }
}
